package client.libmessenger;
import java.util.*;
import java.io.*;
import common.*;
// client connections:

// STATUS CHANGE
// username
// Status[int]

public class LibMessenger 
{
	class UserConnectInfo
	{
		public String userIP = "";
		public int listeningPort = -1;
	}
		
	public String lastError = "";
	public MSNListener listener = null;
	
	public ArrayList<Integer> initial_statuses = null;
	public ArrayList<String> initial_contacts = null;
	public ArrayList<String> initial_groups = null;
	public ArrayList<Boolean> initial_blocked = null;
	
	protected static LibMessenger instance = null;
	protected SocketWrapper connectionToServer = null;			
	protected ConnectionsListener connectionsListenerThread = null;	
	
	protected String username = "";
	
	public LibMessenger()
	{
		super();
	}
	
	protected boolean checkResponse(SocketWrapper wrapper) throws Exception
	{
		try
		{
			String line = wrapper.readLine();
			System.out.println("Response line: "+line);
			
			if(line.indexOf("+OK") == 0)
				return true;
			else
			{
				lastError = line;
				return false;
			} // ELSE
			
		} // TRY
		catch(Exception e)
		{
			throw e;
		} // CATCH		
	}	
	
	protected boolean checkResponse() throws Exception
	{
		return checkResponse(connectionToServer);
	}
	
	public boolean connect(String server, String username, String password)
	{						
		try
		{
			this.username = username;
			connectionToServer = SocketWrapper.connect(server, Constants.SERVER_PORT);
			if (!checkResponse()) return false;
			
			connectionToServer.println(username);
			connectionToServer.println(password);
			
			// TODO: hack for multiple clients, to be removed
			int listeningPort = new Integer(connectionToServer.readLine()).intValue();
			
			if (!checkResponse()) return false;						
			
			// Read the contacts list
			this.initial_contacts = new ArrayList<String>();
			this.initial_statuses = new ArrayList<Integer>();
			this.initial_groups = new ArrayList<String>();
			this.initial_blocked = new ArrayList<Boolean>();
			
			while(true)
			{
				String line1 = connectionToServer.readLine();								
				
				if(line1.indexOf("+OK") == 0)
					break;
				else
				{
					String line2 = connectionToServer.readLine();
					String line3 = connectionToServer.readLine();
					String line4 = connectionToServer.readLine();
					
					System.out.println("line 4 = "+line4);
					
					this.initial_contacts.add(line1);
					this.initial_groups.add(line2);
					this.initial_statuses.add(Integer.valueOf(line3));
					this.initial_blocked.add(new Boolean(line4.equals("1")));
				} // ELSE
			}
			
			// Start listening to incoming connections from other clients and possibly the server
			this.connectionsListenerThread = new ConnectionsListener(listeningPort); 
			this.connectionsListenerThread.start();
			
			instance = this;
			return true;
		} // TRY
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::Connect():");
			e.printStackTrace();
			return false;
		}
	}
	
	public boolean disconnect()
	{
		try
		{
			connectionToServer.println("QUIT");
			connectionsListenerThread.interrupt();
			connectionsListenerThread.socket.close();
			return checkResponse();
		} // TRY
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::disconnect():");
			e.printStackTrace();
			return false;
		} // CATCH				
	}
	
	public boolean blockUser(String username)
	{
		try
		{
			connectionToServer.println("BLOCK");
			connectionToServer.println(username);
			return checkResponse();
		}
		catch(Exception e)
		{
			System.out.println("In LibMessenger::blockUser():");
			e.printStackTrace();
			return false;
		}
	}
	
	public boolean unblockUser(String username)
	{
		try
		{
			connectionToServer.println("UNBLOCK");
			connectionToServer.println(username);
			return checkResponse();
		}
		catch(Exception e)
		{
			System.out.println("In LibMessenger::unblockUser():");
			e.printStackTrace();
			return false;
		}
	}

	protected boolean getUserListeningAddress(String username, UserConnectInfo info) throws Exception
	{
		try
		{
			connectionToServer.println("GET IP");
			connectionToServer.println(username);
			
			if(checkResponse())
			{
				//return connectionToServer.readLine();
				info.userIP = connectionToServer.readLine();
				info.listeningPort = Integer.valueOf(connectionToServer.readLine()).intValue();
				return true;
			}
			else
			{
				return false;
				//throw new Exception(this.lastError);				
			} // ELSE			
		} // TRY
		catch(Exception e)
		{
			throw e;
		}								
	}	
	
	public boolean changeStatus(int status)
	{
		try
		{
			connectionToServer.println("CHANGE STATUS");
			connectionToServer.println(String.valueOf(status));
			return checkResponse();
		} // TRY
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::changeStatus():");
			e.printStackTrace();
			return false;
		} // CATCH							
	}
	
	public boolean sendMessage(String source, String destination, String message)
	{
		try
		{
			UserConnectInfo info = new UserConnectInfo();
			
			if(getUserListeningAddress(destination, info))
			{			
				System.out.println("Sending a message to destination "+info.userIP+" "+info.listeningPort);
				
				SocketWrapper connectionToClient = SocketWrapper.connect(info.userIP, info.listeningPort);			
				connectionToClient.println("MESSAGE");
				connectionToClient.println(source);
				connectionToClient.println(String.valueOf(message.length()));
				connectionToClient.println(message);
				connectionToClient.close();
				return true;
			}
			else
				return false;

		}
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::sendMessage():");
			e.printStackTrace();
			return false;
		}				
	}
	
	public ArrayList<String> searchContacts(String regexp)
	{
		String line;
		ArrayList<String> result = new ArrayList<String>();
		
		try
		{
			connectionToServer.println("REGEXP SEARCH");
			connectionToServer.println(regexp);
			
			while(true)
			{
				line = connectionToServer.readLine();				
				if(line == null || line.indexOf("+OK") == 0) break;
				result.add(line);
			} // WHILE
		}
		catch(Exception e)
		{
			System.out.println("In LibMessenger::searchContacts():");
			e.printStackTrace();			
		}
		
		return result;
	}
	
	public boolean addContact(String username, AddedContactInfo info)
	{
		try
		{
			connectionToServer.println("ADD CONTACT");
			connectionToServer.println(username);
			
			System.out.println("step 0");
			
			if(checkResponse())
			{
				System.out.println("step 1");
				info.status = Integer.valueOf(connectionToServer.readLine());
				System.out.println("step 2");
				return true;
			} // IF
			else
			{
				System.out.println("step 3");
				return false;
			}
		}
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::addContact():");
			e.printStackTrace();
			return false;
		}				
	}
	
	public boolean removeContact(String username)
	{
		try
		{
			connectionToServer.println("REMOVE CONTACT");
			connectionToServer.println(username);
			return checkResponse();
		}
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::removeContact():");
			e.printStackTrace();
			return false;
		}				
	}	
	
	public boolean beginReceivingFile(String fromUserName, String filename, String localFileName)
	{
		try
		{
			UserConnectInfo info = new UserConnectInfo();
			
			getUserListeningAddress(fromUserName, info);			
			System.out.println("Requesting the file "+filename+" from user "+fromUserName+" at IP "+info.userIP);
			
			SocketWrapper connectionToClient = SocketWrapper.connect(info.userIP, info.listeningPort);			
			connectionToClient.println("FILE TRANSFER");
			connectionToClient.println(filename);
			connectionToClient.println(this.username);

			if (!checkResponse(connectionToClient)) return false;			
			int fileSize = Integer.valueOf(connectionToClient.readLine()).intValue();
			
			/*
			FileWriter writer = new FileWriter(new File(localFileName));
			
			for(int i=1; i<=fileSize; ++i)
			{
				int AChar = connectionToClient.in.read();
				writer.append((char) AChar);				
				this.listener.onFileReceiveProgress(fromUserName, filename, fileSize, i);
			} // FOR
			*/
			DataInputStream reader = new DataInputStream(connectionToClient.socket.getInputStream());
			//InputStreamReader reader = new InputStreamReader(connectionToClient.socket.getInputStream());
			FileOutputStream out = new FileOutputStream(localFileName);
			
			System.out.println("receiving remote file "+filename+" into local file "+localFileName);
			
			for(int i=1; i<=fileSize; ++i)
			{
				
				byte AChar = reader.readByte();
				out.write(AChar);
				this.listener.onFileReceiveProgress(fromUserName, filename, fileSize, i);
			} // FOR
			
			reader.close();
			out.flush(); out.close();			
			connectionToClient.close();
			return true;
			
		}
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::beginReceivingFile():");
			e.printStackTrace();
			return false;
		}				
		
	}
	
	public boolean sendGameMove(String destinationUsename, String gameName, String move)
	{
		try
		{
			UserConnectInfo info = new UserConnectInfo();
			getUserListeningAddress(destinationUsename, info);			
			SocketWrapper connectionToClient = SocketWrapper.connect(info.userIP, info.listeningPort);
			
			connectionToClient.println("GAME MOVE");
			connectionToClient.println(this.username);
			connectionToClient.println(gameName);
			connectionToClient.println(move);
			connectionToClient.close();
			
			return true;
			
		}
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::sendGameMove():");
			e.printStackTrace();
			return false;
		}						
	}
	
	public boolean initiateGame(String username, String gameName)
	{
		try
		{
			System.out.println("libmessenger: initiating game from "+this.username+" to "+username);
			
			UserConnectInfo info = new UserConnectInfo();
			getUserListeningAddress(username, info);			
			SocketWrapper connectionToClient = SocketWrapper.connect(info.userIP, info.listeningPort);
			
			connectionToClient.println("GAME START");
			connectionToClient.println(this.username);
			connectionToClient.println(gameName);
			connectionToClient.close();
			
			return true;
			
		}
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::initiateGame():");
			e.printStackTrace();
			return false;
		}						
	}
	
	public boolean offerToSendFile(String destUserName, String filename)
	{
		try
		{
			File file = new File(filename);
			
			if(!file.exists())
			{
				lastError = "file does not exist";
				return false;
			} // IF						
			
			UserConnectInfo info = new UserConnectInfo();
			getUserListeningAddress(destUserName, info);			
			System.out.println("Offering to send a file to user "+destUserName+" at IP "+info.userIP);
			
			SocketWrapper connectionToClient = SocketWrapper.connect(info.userIP, info.listeningPort);			
			connectionToClient.println("FILE TRANSFER PERMISSION");
			connectionToClient.println(this.username);
			//connectionToClient.println(filename.substring(0, filename.lastIndexOf(File.pathSeparatorChar)));
			connectionToClient.println(filename);
			connectionToClient.println(String.valueOf(file.length()));
			connectionToClient.close();
			
			return true;			
		} // TRY
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::offerToSendFile():");
			e.printStackTrace();
			return false;
		} // CATCH				
	}
	
	public boolean assignContactToGroup(String username, String newGroup)
	{
		try
		{
			connectionToServer.println("ASSIGN TO GROUP");
			connectionToServer.println(username);
			connectionToServer.println(newGroup);
			
			return checkResponse();
		} // TRY
		catch(Exception e)
		{
			lastError = e.getMessage();
			System.err.println("In LibMessenger::assignContactToGroup():");
			e.printStackTrace();
			return false;
		} // CATCH				
		
	}
}
